package com.htj.erp.util;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.htj.erp.exception.BusinessException;
import com.htj.erp.model.jpa.RouteDis;
import com.htj.erp.service.RouteDisManger;
import com.htj.erp.vo.LocationVo;
import com.kedacom.utils.HttpUtil;
import com.kedacom.utils.StringUtil;

public class BaiduMapUtil {
    
    private static  final String baiduAk = "052618d685d3176f0efa6f65a18a59bf";
    
    private static final String directionApi = "http://api.map.baidu.com/direction/v1";
    private static final String placeApi = "http://api.map.baidu.com/place/v2/search";
    
    /**
     * 返回数据
     * 格式如下：
     * direction(这里用一句话描述这个方法的作用)
     * (这里描述这个方法适用条件 – 可选)
     * @param orgin
     * @param orginRegion
     * @param destination
     * @param destinationRegion
     * @return
     * @throws Exception
     *String
     * @exception
     * @since  1.0.0
     */
    private static String direction(String orgin,String orginRegion,String destination,String destinationRegion) throws Exception
    {
        StringBuilder url = new StringBuilder(directionApi);
        url.append("?mode=driving");
        url.append("&origin="+ orgin);
        url.append("&origin_region="+ orginRegion);
        url.append("&destination="+ destination);
        url.append("&destination_region="+ destinationRegion);
        url.append("&output=json&ak=" + baiduAk); 
        
        return  HttpUtil.httpGet(url.toString() );
    }
    /**
     * 解析地址
     * parseDirectionDistance(这里用一句话描述这个方法的作用)
     * (这里描述这个方法适用条件 – 可选)
     * @param result
     * @return
     *Double
     * @exception
     * @since  1.0.0
     */
    private static Integer parseDirectionDistance(String result) throws Exception
    {
        JSONObject jo = JSONObject.parseObject(result);
        if(jo.getInteger("status") != 0)
        {
            throw new BusinessException("查询错误" + jo.getString("message"));
        }
        if(jo.getJSONObject("result") != null)
        {
            JSONArray ja = jo.getJSONObject("result").getJSONArray("routes");
            if(ja.size() > 0)
            {
                return ja.getJSONObject(0).getInteger("distance");
            }
        }
        throw new BusinessException("查询错误" + jo.getString("message"));
    }
    
    public static Integer getDistance(String orgin,String orginRegion,String destination,String destinationRegion) throws Exception
    {
        String result = direction(URLEncoder.encode(orgin,"utf-8"),URLEncoder.encode(orginRegion,"utf-8"),
                URLEncoder.encode(destination,"utf-8"),URLEncoder.encode(destinationRegion,"utf-8"));
      
        return parseDirectionDistance(result);
    }
    /**
     * 根据经纬度
     * directionByTude(这里用一句话描述这个方法的作用)
     * (这里描述这个方法适用条件 – 可选)
     * @param orgin
     * @param orginRegion
     * @param destination
     * @param destinationRegion
     * @throws Exception
     *void
     * @exception
     * @since  1.0.0
     */
    public static Integer getDistanceByTude(String orgin,String orginRegion,String destination,String destinationRegion) throws Exception
    {
    	if(orgin.equals(destination))
    	{
    		return 0;
    	}
        String result = direction( orgin ,URLEncoder.encode(orginRegion,"utf-8"),
                destination ,URLEncoder.encode(destinationRegion,"utf-8"));
        
        return parseDirectionDistance(result);
        
    }
    
    private static List<String[]> getReginLngLat(String location,String region)  throws Exception
    {
        StringBuilder url = new StringBuilder(placeApi);
        url.append("?output=json&ak="+baiduAk);
        url.append("&region=" + URLEncoder.encode(region,"utf-8"));
        url.append("&q="+URLEncoder.encode(location,"utf-8"));
        String result = HttpUtil.httpGet(url.toString() );
        JSONArray ja = JSONObject.parseObject(result).getJSONArray("results");
        List<String[]> list = new ArrayList<String[]>();
        for(int i = 0; i < ja.size(); i++)
        {
            JSONObject jo = ja.getJSONObject(i);
            if(jo.getJSONObject("location") != null)
                list.add(new String[]{jo.getJSONObject("location").getString("lng"),jo.getJSONObject("location").getString("lat")});
        }
        return list;
    }
    
    public static String[] determineLngLat(LocationVo vo) throws Exception
    {
        List<String[]> byName = new ArrayList<String[]>(); 
        List<String[]> byAdd = new ArrayList<String[]>(); 
        if(StringUtil.isNotEmpty(vo.getName()))
        {
            byName.addAll(getReginLngLat(vo.getName(),vo.getRegion()));
        }
        
        if(StringUtil.isNotEmpty(vo.getAddress()))
        {
            byAdd.addAll(getReginLngLat(vo.getAddress(),vo.getRegion()));
        }
        
        if(byName.isEmpty())
        {
            if(byAdd.isEmpty())
            {
                throw new BusinessException(vo.getName() + "没有找到地址");
            }
            else if (byAdd.size() >= 1)
            {
                return byAdd.get(0);
            }
            else
            {
                throw new BusinessException(vo.getName() + "地址存在多个");
            }
        }
        else if(byName.size() == 1)
        {
            return byName.get(0);
        }
        else
        {
            if(byAdd.isEmpty())
            {
            	return byName.get(0);
            }
            else if (byAdd.size() == 1)
            {
                return byAdd.get(0);
            }
            else
            {
                for(String[] bn : byName)
                {
                    
                    for(String[] ba : byAdd)
                    {
                        if(ba[0].equals(bn[0]) && ba[1].equals(bn[1]))
                        {
                            return ba;
                        }
                    }
                }
                throw new BusinessException(vo.getName() + "地址存在多个");
            }
            
//            throw new BusinessException(vo.getName() + "按照name和add没有匹配");
        }
        
    }
    
    
    private static  void fillLntLat(LocationVo loc) throws Exception
    {
        if(StringUtil.isEmpty(loc.getLongitude()) || StringUtil.isEmpty(loc.getLatitude()))
        {
            String[] lngLat =  determineLngLat(loc);
            loc.setLongitude(lngLat[0]);
            loc.setLatitude(lngLat[1]);
        }
        
    }
    
    public static PathVo routePlanning(LocationVo start, List<LocationVo> pass, LocationVo end ) throws Exception
    {
        fillLntLat(start);
        for(LocationVo vo : pass)
        {
            fillLntLat(vo);
        }
        fillLntLat(end);
        
        List<LocationVo> allList = new ArrayList<LocationVo>();
        allList.add(start);
        allList.addAll(pass);
        allList.add(end);
        
        Integer[][] dis = new Integer[allList.size() ][allList.size() ];
        for(int i =0; i < dis.length; i++)
        {
            for(int j =0; j<dis[i].length; j++)
            {
                if(i==j)
                {
                    dis[i][j] = 0;
                    continue;
                }
                LocationVo s = allList.get(i);
                LocationVo e = allList.get(j);
                
                
                Integer d = getDistanceByTude(s.getLatitude()+"," + s.getLongitude(), s.getRegion(),
                        e.getLatitude()+"," + e.getLongitude(), e.getRegion()
                        );
                dis[i][j] = d;
                
            }
        }
        
        System.out.println("距离计算完毕。");
        //从最远的开始送
        Integer[] startDis = dis[0];
        
        Integer selIdx = 0;
        Integer selVal = 0;
        
        for(int i = 0; i < startDis.length; i++)
        {
            if(startDis[i] > selVal)
            {
                selIdx = i;
                selVal = startDis[i];
            }
        }
        
        int startIdx = selIdx;
        int endIdx = dis.length -1;
        
        List<Integer> tPath = new ArrayList<Integer>();
        for(int i =0; i < dis.length; i++)
        {
            tPath.add(i);
        }
        
        tPath.remove(endIdx);
        tPath.remove(startIdx);
        tPath.remove(0);
        PathVo vo = calDis(startIdx, tPath,endIdx,dis );
        
        String[] ps = vo.path.split("->");
        ArrayList<LocationVo> resList = new ArrayList<LocationVo>();
        resList.add(allList.get(0));
        resList.add(allList.get(startIdx));
        
        
        System.out.println(allList.get(0).getName());
        System.out.println(allList.get(startIdx).getName());
        for(String p : ps)
        {
            System.out.println(allList.get(Integer.parseInt(p)).getName());
            resList.add(allList.get(Integer.parseInt(p)));
        }
        vo.distance +=selVal;
        System.out.println("总里程：" + (vo.distance ));
        vo.loc = resList;
        return vo;
        
        
    }
    public static PathVo routePlanningNew(LocationVo start, List<LocationVo> pass, LocationVo end,boolean usingMaxDis,RouteDisManger routeDisManger) throws Exception
    {
        fillLntLat(start);
        for(LocationVo vo : pass)
        {
            fillLntLat(vo);
        }
        fillLntLat(end);
        
        List<LocationVo> allList = new ArrayList<LocationVo>();
        allList.add(start);
        allList.addAll(pass);
        allList.add(end);
        
        Map<LocationVo,Map<LocationVo,Integer>> disMap= new HashMap<LocationVo,Map<LocationVo,Integer>>();
        
        for(int i =0; i < allList.size(); i++)
        {
        	LocationVo v1 = allList.get(i);
        	if(!disMap.containsKey(v1))
        	{
        		disMap.put(v1, new HashMap<LocationVo,Integer>());
        	}
        	
        	Map<LocationVo,Integer> v1Map = disMap.get(v1);
        	
            for(int j =0; j<allList.size(); j++)
            {
            	LocationVo v2 = allList.get(j);
            	
            	Integer curDis = 0;
                if(i!=j)
                {
                	LocationVo s = allList.get(i);
                	LocationVo e = allList.get(j);
                	if(routeDisManger != null)
                    {
                    	 RouteDis rd = routeDisManger.getDis(s, e);
                    	 if(rd != null)
                    	 {
                    		 curDis = rd.getDistance();
                    	 }
                    }
                	if(curDis == null || curDis == 0 )
                	{
                		curDis = getDistanceByTude(s.getLatitude()+"," + s.getLongitude(), s.getRegion(),
                				e.getLatitude()+"," + e.getLongitude(), e.getRegion()
                				);
                		if(routeDisManger != null)
                        {
                			routeDisManger.saveDis(s, e,curDis);
                        }
                	}
                	
                }
                v1Map.put(v2, curDis);
            }
        }
        
        System.out.println("距离计算完毕。");
        
        
        Map<LocationVo,Integer> startDis =  disMap.get(start);
        Integer maxDis = 0;
        LocationVo maxDisLoc = null;
        for(LocationVo vo : startDis.keySet())
        {
        	if(startDis.get(vo) > maxDis)
            {
        		maxDis = startDis.get(vo);
        		maxDisLoc = vo;
            }
        }
        //剩余需要去遍历的数据
        List<LocationVo> restLoc = new ArrayList<LocationVo>();
        for(int i =0; i < allList.size(); i++)
        {
        	restLoc.add(allList.get(i));
        }
        if(usingMaxDis)
        {
        	//去掉一些。
            restLoc.remove(restLoc.size()-1);
            restLoc.remove(maxDisLoc);
            restLoc.remove(0);
            //记录已经计算是你过的最短路径
            Map<RoutePath,PathVo> miniDistance = new HashMap<RoutePath,PathVo>();
            PathVo vo = calDis(maxDisLoc, restLoc,end,disMap,0,miniDistance );
            
            ArrayList<LocationVo> resList = new ArrayList<LocationVo>();
            //路程汇总， 起点，到最远距离
            resList.add(start);
            resList.add(maxDisLoc);
            resList.addAll(vo.loc);
            // 增加到最远距离的路程。
            vo.distance += maxDis;
            System.out.println("最远开始总里程：" + (vo.distance ));
            vo.loc = resList;
            return vo;
        }
        else
        {
        	//去掉一些。
            restLoc.remove(restLoc.size()-1);
//            restLoc.remove(maxDisLoc);
            restLoc.remove(0);
            //记录已经计算是你过的最短路径
            Map<RoutePath,PathVo> miniDistance = new HashMap<RoutePath,PathVo>();
            PathVo vo = calDis(start, restLoc,end,disMap,0,miniDistance );
            
            ArrayList<LocationVo> resList = new ArrayList<LocationVo>();
            //last dis
            Integer disLast = disMap.get(start).get(vo.getLoc().get(vo.getLoc().size()-2));
            Integer disFirst = disMap.get(start).get(vo.getLoc().get(0));
            resList.add(start);
            if(disLast > disFirst)
            {
            	for(int i =vo.getLoc().size() -2;i>=0;i--)
            	{
            		resList.add(vo.getLoc().get(i));
            	}
            	resList.add(vo.getLoc().get(vo.getLoc().size()-1));
            }
            else
            {
            	resList.addAll(vo.loc);
            }
            //路程汇总， 起点，到最远距离
            System.out.println("最少路程总里程：" + (vo.distance ));
            vo.loc = resList;
            return vo;
        }
        
        
        
    }
    
    public static boolean equalList(List list1, List list2) {
        if (list1.size() != list2.size())
            return false;
        if (list2.containsAll(list1))
            return true;
        return false;
    }
    
    
    
    private static  PathVo calDis(LocationVo startLoc,List<LocationVo> restLoc,LocationVo endLoc,Map<LocationVo,
    		Map<LocationVo,Integer>> disMap,Integer reIdx, Map<RoutePath,PathVo> miniDistance)
    {
    	System.out.println("reIdx=" + reIdx + " calDis=startIdx:" + startLoc.getName() + ":endIdx=" + endLoc.getName() + ":=" + restLoc.size());
        PathVo  vo = new PathVo();
        if(restLoc.isEmpty())
        {
            vo.loc.add(endLoc);
            vo.distance = disMap.get(startLoc).get(endLoc);
            return vo;
        }
        RoutePath routePath = new RoutePath();
        routePath.start = startLoc;
        routePath.pathList = restLoc;
        if(miniDistance.containsKey(routePath))
        	return miniDistance.get(routePath);
        
        PathVo[] tmpDis = new PathVo[restLoc.size()];
        
        for(int i = 0; i < restLoc.size(); i++)
        {
        	LocationVo nextLoc = restLoc.get(i);
        	//和下个之间的距离
            Integer t1 = disMap.get(startLoc).get(nextLoc);
            
            List<LocationVo> tPath = new ArrayList<LocationVo>();
            tPath.addAll(restLoc);
            tPath.remove(nextLoc);
            
            PathVo  t2  = calDis(nextLoc,tPath,endLoc,disMap,reIdx+1,miniDistance);
            
            PathVo t = new PathVo();
            t.distance = t1+t2.distance;
            //路径加上
            t.loc.add(nextLoc);
            t.loc.addAll(t2.loc);
            tmpDis[i] = t;
        }
        
        PathVo miniNextPath = null;
        Integer miniDis = Integer.MAX_VALUE;
        for(int i = 0; i < tmpDis.length; i++)
        {
            if(tmpDis[i].distance < miniDis)
            {
            	miniNextPath = tmpDis[i];
            	miniDis = tmpDis[i].distance;
            }
        }
        miniDistance.put(routePath, miniNextPath);
        return miniNextPath;
    }
    
    public static  PathVo calDis(Integer startIdx,List<Integer> tmpPath,Integer endIdx,Integer[][] dis)
    {
    	System.out.println("calDis=startIdx:" + startIdx + ":endIdx=" + endIdx + ":=" + tmpPath.size());
        PathVo  vo = new PathVo();
        if(tmpPath.isEmpty())
        {
            vo.path= "" + endIdx;
            vo.distance = dis[startIdx][endIdx];
            return vo;
        }
        PathVo[] tmpDis = new PathVo[tmpPath.size()];
        
        for(int i = 0; i < tmpPath.size(); i++)
        {
            Integer si = tmpPath.get(i);
            Integer t1 = dis[startIdx][si];
            List<Integer> tPath = new ArrayList<Integer>();
            tPath.addAll(tmpPath);
            tPath.remove(i);
            PathVo  t2  = calDis(si,tPath,endIdx,dis);
            
            PathVo t = new PathVo();
            t.distance = t1+t2.distance;
            t.path = si +"->" +t2.path;
            tmpDis[i] = t;
        }
        
        Integer selIdx = 0;
        Integer selVal = Integer.MAX_VALUE;
        for(int i = 0; i < tmpDis.length; i++)
        {
            if(tmpDis[i].distance < selVal)
            {
                selIdx = i;
                selVal = tmpDis[i].distance;
            }
        }
        
        return tmpDis[selIdx];
    }
    
    
    
    
    public static void main(String[] args)
    {
        try {
        	LocationVo vo = new LocationVo();
    		vo.setAddress("张家港市中兴街心公园1号（中心小学旁）");
    		vo.setName("张家港市中兴邮政支局");
    		vo.setRegion("张家港");
    		String[] ll = BaiduMapUtil.determineLngLat(vo);
    		System.out.println(ll[0] + "," + ll[1]);
//            LocationVo start = new LocationVo("苏州", "科达科技股份有限公司", "金山路131号");
//            LocationVo p1 = new LocationVo("苏州", "苏州姑苏分局阊门邮政支局", "广济路28号");
//            LocationVo p2 = new LocationVo("苏州", "苏州姑苏分局醋库巷邮政支局", "醋库巷2号");
//            LocationVo p3 = new LocationVo("苏州", "苏州吴中分局长桥邮政支局", "苏州市吴中区宝带东路335号");
//            LocationVo p4 = new LocationVo("苏州", "太仓市浏家港邮政支局", "太仓市浏家港飞马路302号（茜中路联华超市对面）");
//            LocationVo p5 = new LocationVo("苏州", "太仓市浏家港邮政支局", "太仓市浏家港飞马路302号（茜中路联华超市对面）");
//            LocationVo p6 = new LocationVo("苏州", "吴江市坛丘邮政支局", "吴江区坛丘镇花园新村");
//            LocationVo p7 = new LocationVo("苏州", "吴江市菀坪邮政支局", "吴江区菀坪开发路7号邮政支局内");
//            List<LocationVo> pass = new ArrayList<LocationVo>();
//            pass.add(p5);
//            pass.add(p6);
//            pass.add(p3);
//            pass.add(p1);
//            pass.add(p2);
//            pass.add(p4);
//            pass.add(p7);
//            PathVo v1 = routePlanning(start,pass,start);
//            PathVo v2 = routePlanningNew(start,pass,start,null);
//            System.out.println("routePlanning dis = " + v1.distance);
//            for(LocationVo lv : v1.loc)
//            {
//                System.out.println(lv.getAddress());
//            }
//            System.out.println("=========================");
//            System.out.println("routePlanningNew dis = " + v2.distance);
//            for(LocationVo lv : v2.loc)
//            {
//                System.out.println(lv.getAddress());
//            }
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    
    
}
/**
 * 判断到目的地最短距离的集合,只要开始一样,经过路径一样即可.找到最短的.
 * @author Administrator
 *
 */
class RoutePath{
	LocationVo start;
	List<LocationVo> pathList;
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((pathList == null) ? 0 : pathList.hashCode());
		result = prime * result + ((start == null) ? 0 : start.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		RoutePath other = (RoutePath) obj;
		if (pathList == null) {
			if (other.pathList != null)
				return false;
		} else if (!BaiduMapUtil.equalList(other.pathList, pathList))
			return false;
		if (start == null) {
			if (other.start != null)
				return false;
		} else if (!start.equals(other.start))
			return false;
		return true;
	}
	 
	
	
}
